﻿using System;
using System.Collections.Generic;
using System.Threading;
using log4net;

namespace Interactive.Utility.Cache
{
    /// <summary>
    /// 永久内存,不定期刷新
    /// </summary>
    public class EternallyCacheProxy<V> : ICacheProxy<V>
    {
        /// <summary>
        /// 日志记录器
        /// </summary>
        private static readonly ILog myLog = LogManager.GetLogger("EternallyCacheProxy_V");

        /// <summary>
        /// 向外界表明繁忙,获取失败,有一个线程正在做
        /// </summary>
        public readonly V DefaultBusyValue;

        /// <summary>
        /// 默认的获取方法
        /// </summary>
        private readonly Func<V> DefaultFunc;

        /// <summary>
        /// 锁
        /// </summary>
        private readonly object myLock = new object();

        /// <summary>
        /// 在等待获取值时的等待时间
        /// </summary>
        private readonly int waitMillseconds;

        /// <summary>
        /// 缓存值
        /// </summary>
        private V cacheValue;

        /// <summary>
        /// 是否已经初始化值
        /// </summary>
        private bool isGet;

        #region Implementation of ICacheProxy<V>

        /// <summary>
        /// 获取值
        /// </summary>
        public V Value
        {
            get
            {
                if (!isGet)
                {
                    if (Monitor.TryEnter(myLock, waitMillseconds))
                    {
                        try
                        {
                            if (!isGet)
                            {
                                cacheValue = DefaultFunc();
                                isGet = true;
                            }
                        }
                        catch (Exception exception)
                        {
                            myLog.Error("在获取缓存值的时候出现错误!", exception);
                        }
                        finally
                        {
                            Monitor.Exit(myLock);
                        }
                    }

                    else return DefaultBusyValue;
                }
                return cacheValue;
            }
        }

        /// <summary>
        /// 强制更新缓存,无限等待
        /// </summary>
        /// <returns></returns>
        public bool EnforceRefresh()
        {
            if (isGet)
            {
                lock (myLock)
                {
                    if (isGet)
                        isGet = false;
                }
            }

            return true;
        }

        /// <summary>
        /// 强制更新缓存,在Eternally中,没有等待毫秒数的概念
        /// 所有的线程都会执行更新操作
        /// </summary>
        /// <param name="millseconds">等待的毫秒数</param>
        /// <returns></returns>
        public bool EnforceRefresh(int millseconds)
        {
            return EnforceRefresh();
        }

        #endregion

        public EternallyCacheProxy(Func<V> defaultFunc, V defaultBusyValue, int waitMillseconds, V initValue)
            : this(defaultFunc, defaultBusyValue, waitMillseconds, false, initValue)
        {
        }

        public EternallyCacheProxy(Func<V> defaultFunc, V defaultBusyValue, int waitMillseconds)
            : this(defaultFunc, defaultBusyValue, waitMillseconds, true, default(V))
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="defaultFunc">获取的方法</param>
        /// <param name="waitMillseconds">等待时间,默认50毫秒</param>
        /// <param name="isLazyGet">是否是懒初始化,true是,false否</param>
        /// <param name="defaultBusyValue">标识繁忙的值</param>
        /// <param name="initValue">当为否的时候需要提供初始值</param>
        private EternallyCacheProxy(Func<V> defaultFunc, V defaultBusyValue, int waitMillseconds, bool isLazyGet,
                                    V initValue)
        {
            if (!isLazyGet)
            {
                cacheValue = initValue;
                isGet = true;
            }
            DefaultBusyValue = defaultBusyValue;
            DefaultFunc = defaultFunc;
            this.waitMillseconds = waitMillseconds;
        }


        /// <summary>
        /// 用一个现有值更新缓存
        /// </summary>
        /// <param name="defaultValue">现有值</param>
        public void EnforceRefresh(V defaultValue)
        {
            lock (myLock)
            {
                cacheValue = defaultValue;
                isGet = true;
            }
        }
    }


    /// <summary>
    /// 永久内存,不定期刷新
    /// </summary>
    public class EternallyCacheProxy<K, V> : ICacheProxy<K, V>
    {
        /// <summary>
        /// 日志记录器
        /// </summary>
        private static readonly ILog myLog = LogManager.GetLogger("EternallyCacheProxy_KV");


        /// <summary>
        /// 向外界表明繁忙,获取失败,有一个线程正在做
        /// </summary>
        public readonly V DefaultBusyValue;

        /// <summary>
        /// 默认的获取方法
        /// </summary>
        private readonly Func<K, V> DefaultFunc;

        /// <summary>
        /// 初始容量
        /// </summary>
        private readonly int capacity;

        /// <summary>
        /// 容器
        /// </summary>
        private readonly Dictionary<K, CacheWrapper> dictionary;

        /// <summary>
        /// 锁
        /// </summary>
        private readonly object myLock = new object();

        /// <summary>
        /// 在等待获取值时的等待时间
        /// </summary>
        private readonly int waitMillseconds;

        #region Implementation of ICacheProxy<K, V>

        public V this[K key]
        {
            get { return GetValue(key); }
            set
            {
                CacheWrapper temp;
                if (!dictionary.TryGetValue(key, out temp))
                {
                    lock (myLock)
                    {
                        if (!dictionary.TryGetValue(key, out temp))
                        {
                            temp = new CacheWrapper(this, value);
                            dictionary.Add(key, temp);
                        }
                        else temp.EnforceRefresh(value);
                    }
                }
                else
                    temp.EnforceRefresh(value);
            }
        }

        /// <summary>
        /// 获取值
        /// </summary>
        public V GetValue(K key)
        {
            CacheWrapper cacheWrapper;
            if (dictionary.TryGetValue(key, out cacheWrapper))
                return cacheWrapper.GetValue(key);

            lock (myLock)
            {
                if (!dictionary.TryGetValue(key, out cacheWrapper))
                {
                    cacheWrapper = new CacheWrapper(this);
                    dictionary.Add(key, cacheWrapper);
                }
            }
            return cacheWrapper.GetValue(key);
        }


        /// <summary>
        /// 强制更新缓存,无限期等待
        /// </summary>
        /// <param name="key">键</param>
        /// <returns>结果</returns>
        public bool EnforceRefresh(K key)
        {
            CacheWrapper temp;
            if (dictionary.TryGetValue(key, out temp)) temp.EnforceRefresh();
            return true;
        }


        /// <summary>
        /// 强制更新缓存,在Eternally中,没有等待毫秒数的概念
        /// 所有的线程都会执行更新操作
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="millseconds">等待的毫秒数</param>
        /// <returns></returns>
        public bool EnforceRefresh(K key, int millseconds)
        {
            return EnforceRefresh(key);
        }

        /// <summary>
        /// 尝试获取
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="value">值</param>
        /// <returns>是否有</returns>
        public bool TryGetValue(K key, out V value)
        {
            CacheWrapper cacheWrapper;
            bool result = dictionary.TryGetValue(key, out cacheWrapper);
            value = result ? cacheWrapper.GetValue(key) : default(V);
            return result;
        }

        /// <summary>
        /// 刷新缓存,用默认值
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="defaultValue">默认值</param>
        /// <example>经过单元测试,对于T为int时有问题,与上面的函数冲突</example>
        public void EnforceRefresh(K key, V defaultValue)
        {
            this[key] = defaultValue;
        }

        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="capacity">容量</param>
        /// <param name="defaultFunc">获取的方法</param>
        /// <param name="defaultBusyValue">标识繁忙的值</param>
        /// <param name="waitMillseconds">等待时间,默认50毫秒</param>
        public EternallyCacheProxy(int capacity, Func<K, V> defaultFunc, V defaultBusyValue, int waitMillseconds)
        {
            this.capacity = capacity;
            dictionary = new Dictionary<K, CacheWrapper>(this.capacity);
            DefaultBusyValue = defaultBusyValue;
            DefaultFunc = defaultFunc;
            this.waitMillseconds = waitMillseconds;
        }

        public EternallyCacheProxy(int capacity, Func<K, V> defaultFunc, V defaultBusyValue)
            : this(capacity, defaultFunc, defaultBusyValue, 50)
        {
        }

        #region Nested type: CacheWrapper

        /// <summary>
        /// 包装器
        /// </summary>
        private class CacheWrapper
        {
            private readonly EternallyCacheProxy<K, V> cacheProxy;

            /// <summary>
            /// 锁
            /// </summary>
            private readonly object myLock = new object();


            /// <summary>
            /// 缓存值
            /// </summary>
            private V cacheValue;

            /// <summary>
            /// 是否已经初始化值
            /// </summary>
            private bool isGet;

            /// <summary>
            /// 构造函数,不会立刻初始化值
            /// </summary>
            /// <param name="proxy">依赖项</param>
            public CacheWrapper(EternallyCacheProxy<K, V> proxy) : this(proxy, true, default(V))
            {
            }

            /// <summary>
            /// 构造函数,用给定的值进行初始化
            /// </summary>
            /// <param name="proxy">依赖项</param>
            /// <param name="initValue">初始值</param>
            public CacheWrapper(EternallyCacheProxy<K, V> proxy, V initValue) : this(proxy, false, initValue)
            {
            }

            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="proxy">依赖项</param>
            /// <param name="isLazyGet">是否是懒初始化,true是,false否</param>
            /// <param name="initValue">当为否的时候需要提供初始值</param>
            private CacheWrapper(EternallyCacheProxy<K, V> proxy, bool isLazyGet, V initValue)
            {
                cacheProxy = proxy;
                if (!isLazyGet)
                {
                    cacheValue = initValue;
                    isGet = true;
                }
            }


            public V GetValue(K k)
            {
                if (!isGet)
                {
                    if (Monitor.TryEnter(myLock, cacheProxy.waitMillseconds))
                    {
                        try
                        {
                            if (!isGet)
                            {
                                cacheValue = cacheProxy.DefaultFunc(k);
                                isGet = true;
                            }
                        }
                        catch (Exception exception)
                        {
                            myLog.Error("在获取缓存值的时候出现错误!", exception);
                        }
                        finally
                        {
                            Monitor.Exit(myLock);
                        }
                    }
                    else return cacheProxy.DefaultBusyValue;
                }
                return cacheValue;
            }

            #region 强制刷新缓存

            public void EnforceRefresh()
            {
                if (isGet)
                {
                    lock (myLock)
                    {
                        if (isGet)
                            isGet = false;
                    }
                }
            }

            public void EnforceRefresh(V defaultValue)
            {
                lock (myLock)
                {
                    isGet = true;
                    cacheValue = defaultValue;
                }
            }

            #endregion
        }

        #endregion
    }
}